// JS API创建工厂，JS API每被调用一次，创建一个实例，该实例保存API回调函数的引用
class XMAPIFactory {
  success;
  fail;

  constructor(invokeID, apiName, param, success, fail) {
    this.success =
      success ||
      function (data) {
        console.log(data);
      };
    this.fail =
      fail ||
      function (msg) {
        console.log(msg);
      };
    _execute(invokeID, apiName, param, 'XMAPICallBack.success', 'XMAPICallBack.fail');
  }
}

class XMAPICallBack {
  callBackObjects;

  constructor() {
    this.callBackObjects = new Map();
  }

  success(invokeID, data) {
    const callback = this.callBackObjects.get(invokeID);
    if (callback) {
      callback.success(data);
      this.callBackObjects.delete(invokeID);
    }
  }

  // 接口执行失败回调
  fail(invokeID, msg) {
    const callback = this.callBackObjects.get(invokeID);
    if (callback) {
      callback.fail(msg);
      this.callBackObjects.delete(invokeID);
    }
  }
}

// 定义全局变量，接口回调函数的引用皆保存于此
window.XMAPICallBack = new XMAPICallBack();

/*
 * JS API最终执行函数，JavaScript和原生功能交互桥梁
 *
 * @param apiName:字符串，接口名
 * @param param:字符串或JSON，调用接口需要传递的参数
 * @param success:字符串，接口调用成功回调函数名
 * @param fail:字符串，接口调用失败回调函数名
 * */
function _execute(invokeID, apiName, param, success, fail) {
  const paramStr = param ? JSON.stringify(param) : '';
  const src =
    'xm-services://?action=jsFun' +
    '&invokeID=' +
    (invokeID || '') +
    '&apiName=' +
    (apiName || '') +
    '&param=' +
    encodeURIComponent(paramStr) +
    '&success=' +
    (success || '') +
    '&fail=' +
    (fail || '');
  const iframe = document.createElement('iframe');
  iframe.src = src;
  iframe.style.width = '0px';
  iframe.style.height = '0px';

  document.body.appendChild(iframe);
  window.setTimeout(() => {
    document.body.removeChild(iframe);
  }, 800);
}

const xm = (name, xmOption) => {
  if (!name) {
    throw new Error('请传入方法名');
  }
  xmOption = xmOption || {};
  xmOption.param = xmOption.param || {};
  const success =
    xmOption.success ||
    function (data) {
      console.log(data);
    };
  const fail =
    xmOption.fail ||
    function (msg) {
      throw new Error(msg);
    };
  const invokeID = name + new Date().getTime();
  const factory = new XMAPIFactory(invokeID, name, xmOption.param, success, fail);
  window.XMAPICallBack.callBackObjects.set(invokeID, factory);
};

export default xm;
